﻿/**
 * File:   setting_slider.c
 * Author: AWTK Develop Team
 * Brief:  setting_slider
 *
 * Copyright (c) 2018 - 2022  Guangzhou ZHIYUAN Electronics Co.,Ltd.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * License file for more details.
 *
 */

/**
 * History:aod
 * 2018-04-02 Li XianJing <xianjimli@hotmail.com> created
 *
 */

#include "tkc/mem.h"
#include "tkc/rect.h"
#include "tkc/utils.h"
#include "tkc/time_now.h"
#include "base/keys.h"
#include "base/widget_vtable.h"
#include "base/image_manager.h"
#include "setting_slider.h"
#include "base/canvas.h"
#include "base/vgcanvas.h"
#include "tkc/tokenizer.h"
#include "tkc/wstr.h"
#include "stdio.h"
#include "stdlib.h" 

#define DRAGGER_WIDTH 24
#define TAG_WIDTH 57
#define TAG_HEIGHT 46
#define SCALE_GAP 10
#define TAG_CIRCLE_WIDTH 47
#define SLIDER_GAP 37

static ret_t setting_slider_update_serial_array(widget_t* widget);
static inline double setting_slider_get_top_y(widget_t *widget);
static inline double setting_slider_get_bottom_y(widget_t *widget);
static inline double setting_slider_get_serial_line_width(widget_t *widget);
static void setting_slider_get_serial_bound_by_value(widget_t* widget, int *lower_idx, int *upper_idx);


static ret_t setting_slider_load_icon(widget_t* widget, bitmap_t* img) {
  style_t* style = widget->astyle;
  const char* image_name = style_get_str(style, STYLE_ID_ICON, NULL);
  if (image_name && widget_load_image(widget, image_name, img) == RET_OK) {
    return RET_OK;
  } else {
    return RET_FAIL;
  }
}

static uint32_t setting_slider_get_bar_size(widget_t* widget) {
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(setting_slider != NULL, 0);

  return setting_slider->bar_size ? setting_slider->bar_size : (widget->w >> 1);
}

static uint32_t setting_slider_get_dragger_size(widget_t* widget) {
  bitmap_t img;
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  uint32_t dragger_size = setting_slider->dragger_size;
  if (setting_slider->auto_get_dragger_size) {
    dragger_size = setting_slider_get_bar_size(widget) * 1.5f;
  }
  if (setting_slider->dragger_adapt_to_icon) {
    float_t ratio = system_info()->device_pixel_ratio;
    if (setting_slider_load_icon(widget, &img) == RET_OK) {
      dragger_size = (img.h / ratio);
    }
  }
  return dragger_size;
}


static inline double setting_slider_get_top_y(widget_t *widget){
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  if(setting_slider == NULL){
    printf("%s: setting slider is NULL!\r\n", __func__);
    return -1;
  }

  uint32_t dragger_size = setting_slider_get_dragger_size(widget);
  return SLIDER_GAP + dragger_size / 2;
}

static inline double setting_slider_get_bottom_y(widget_t *widget){
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  if(setting_slider == NULL){
    printf("%s: setting slider is NULL!\r\n", __func__);
    return -1;
  }
  uint32_t dragger_size = setting_slider_get_dragger_size(widget);
  return widget->h - dragger_size / 2 - SLIDER_GAP;
}

static inline int setting_slider_get_serial_count(widget_t *widget){
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  if(setting_slider == NULL){
    printf("%s: setting slider is NULL!\r\n", __func__);
    return -1;
  }
  int serial_array_len = str_count(&setting_slider->serial, ";");
  serial_array_len = tk_min(serial_array_len, SETTING_SLIDER_MAX_SCALE_NUM);
  return serial_array_len;
}

static inline double setting_slider_get_serial_line_width(widget_t *widget){
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  if(setting_slider == NULL){
    printf("%s: setting slider is NULL!\r\n", __func__);
    return -1;
  }
  int serial_array_len = setting_slider_get_serial_count(widget);
  double end_line = setting_slider_get_bottom_y(widget);
  double start_line = setting_slider_get_top_y(widget);
  double line_width = (serial_array_len == 1) ? 0 : (end_line - start_line) / (serial_array_len - 1);
  return line_width;
}

static void setting_slider_get_serial_bound_by_value(widget_t* widget, int *lower_idx, int *upper_idx)
{
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  if(setting_slider == NULL){
    printf("%s: setting slider is NULL!\r\n", __func__);
    return -1;
  }
  int serial_array_len = setting_slider_get_serial_count(widget);
  *lower_idx = -1;
  *upper_idx = -1;
  // printf("\r\n");

  for(int i = 0; i < serial_array_len - 1; i++){
    double value = setting_slider->value;
    // printf("%d %d %d %d %d\r\n", i, i+1, setting_slider->scales[i].value, setting_slider->scales[i+1].value, value);
    if(setting_slider->scales[i].value == value){
      *lower_idx = i; 
      *upper_idx = i;
      return;
    }
    if(setting_slider->scales[i + 1].value == value){
      *lower_idx = i + 1; 
      *upper_idx = i + 1;
      return;
    }
    if(!setting_slider->is_reverse){
      if(setting_slider->scales[i].value < value && value < setting_slider->scales[i + 1].value){
        *lower_idx = i;
        *upper_idx = i + 1;
        return;
      }
    }
    else{
      if(setting_slider->scales[i + 1].value < value && value < setting_slider->scales[i].value){
        *lower_idx = i + 1;
        *upper_idx = i;
        return;
      }
    }

  }
  printf("%s:no bound found!\r\n", __func__);
}

static void setting_slider_get_serial_bound_by_pos(widget_t* widget, int pos, int *lower_idx, int *upper_idx)
{
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  if(setting_slider == NULL){
    printf("%s: setting slider is NULL!\r\n", __func__);
    return -1;
  }
  int serial_array_len = setting_slider_get_serial_count(widget);
  *lower_idx = -1;
  *upper_idx = -1;
  pos = tk_clamp(pos, setting_slider->scales[0].line_y, setting_slider->scales[serial_array_len - 1].line_y);
  // printf("\r\n");
  for(int i = 0; i < serial_array_len - 1; i++){
    // printf("%d %d %d %d %d\r\n", i, i+1, setting_slider->scales[i].line_y, setting_slider->scales[i+1].line_y, pos);
    if(setting_slider->scales[i].line_y == pos){
      *lower_idx = i; 
      *upper_idx = i;
      return;
    }
    if(setting_slider->scales[i + 1].line_y == pos){
      *lower_idx = i + 1; 
      *upper_idx = i + 1;
      return;
    }
    if(setting_slider->scales[i].line_y < pos && pos < setting_slider->scales[i + 1].line_y){
      *lower_idx = i;
      *upper_idx = i + 1;
      return;
    }
  }
  printf("%s:no bound found!\r\n", __func__);
}

static double setting_slider_pixel_to_value(widget_t* widget, int pos){
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  if(setting_slider == NULL){
    printf("%s: setting slider is NULL!\r\n", __func__);
    return -1;
  }
  int lower_idx = 0, upper_idx = 0;
  setting_slider_get_serial_bound_by_pos(widget, pos, &lower_idx, &upper_idx);
  
  int upper_value = setting_slider->scales[upper_idx].value;
  int lower_value = setting_slider->scales[lower_idx].value;
  int upper_pos = setting_slider->scales[upper_idx].line_y;
  int lower_pos = setting_slider->scales[lower_idx].line_y;

  if(lower_idx == -1 || upper_idx == -1)
    return 0;
  if(lower_idx == upper_idx){
    return upper_value;
  }
  else{
    double value_per_pixel_in_this_range = (upper_value - lower_value) / (double)(upper_pos - lower_pos);
    int remain_pos = pos - lower_pos;
    // printf("upv %d lpv %d upp %d lpp %d ", upper_value, lower_value, upper_pos, lower_pos);
    // printf("rpos %d v_per_p %.2f\r\n", remain_pos, value_per_pixel_in_this_range);
    return lower_value + value_per_pixel_in_this_range * remain_pos;
  }
}

static int setting_slider_value_to_pixel(widget_t* widget, double value){
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  if(setting_slider == NULL){
    printf("%s: setting slider is NULL!\r\n", __func__);
    return -1;
  }
  int lower_idx = 0, upper_idx = 0;
  setting_slider_get_serial_bound_by_value(widget, &lower_idx, &upper_idx);

  int upper_value = setting_slider->scales[upper_idx].value;
  int lower_value = setting_slider->scales[lower_idx].value;
  int upper_pos = setting_slider->scales[upper_idx].line_y;
  int lower_pos = setting_slider->scales[lower_idx].line_y;

  if(lower_idx == -1 || upper_idx == -1)
    return 0;
  if(upper_idx == lower_idx){
    return upper_pos;
  }
  else{
    double remain_value = value - lower_value;
    double pixel_per_value_in_this_range = (upper_pos - lower_pos) / (double)(upper_value - lower_value); 
    // printf("upos %d lpos %d upv %d lpv %d ppv %.2lf\r\n", upper_pos, lower_pos, upper_value, lower_value, pixel_per_value_in_this_range);
    return lower_pos + pixel_per_value_in_this_range * remain_value;
  }
}

static double setting_get_fvalue_from_scale(widget_t* widget) {
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(setting_slider != NULL, -1);
  return 0;
}

static ret_t setting_slider_update_dragger_rect(widget_t* widget, canvas_t* c) {
  rect_t* r = NULL;
  double fvalue = 0;
  int32_t margin = 0;
  uint32_t dragger_size = 0;
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(setting_slider != NULL, RET_BAD_PARAMS);

  r = &(setting_slider->dragger_rect);
  dragger_size = setting_slider_get_dragger_size(widget);

  r->x = (widget->w - DRAGGER_WIDTH) / 2;
  r->w = DRAGGER_WIDTH;
  r->h = dragger_size;
  r->y = setting_slider_value_to_pixel(widget, setting_slider->value) - dragger_size / 2;


  return RET_OK;
}

#ifdef _WIN32
ret_t canvas_save(canvas_t* c) {
  return_value_if_fail(c != NULL && c->lcd != NULL, RET_BAD_PARAMS);

#if defined(AWTK_WEB)
  vgcanvas_save(lcd_get_vgcanvas(c->lcd));
#endif /*AWTK_WEB*/

  return RET_OK;
}

ret_t canvas_restore(canvas_t* c) {
  return_value_if_fail(c != NULL && c->lcd != NULL, RET_BAD_PARAMS);

#if defined(AWTK_WEB)
  vgcanvas_restore(lcd_get_vgcanvas(c->lcd));
#endif /*AWTK_WEB*/

#if defined(WITH_GPU)
  lcd_set_font_name(c->lcd, c->font_name);
  lcd_set_font_size(c->lcd, c->font_size);
#endif /*WITH_GPU*/

  return RET_OK;
}
#endif 

static ret_t setting_slider_fill_fg_rounded_rect_by_butt(canvas_t* c, rect_t fg_rect,
                                                         rect_t* bg_rect, const color_t* color,
                                                         uint32_t radius) {
  rect_t r_save = {0};
  rect_t r_vg_save = {0};
  vgcanvas_t* vg = canvas_get_vgcanvas(c);
  canvas_save(c);
  canvas_get_clip_rect(c, &r_save);
  if (vg != NULL) {
    vgcanvas_save(vg);
    r_vg_save = rect_from_rectf(vgcanvas_get_clip_rect(vg));
  }

  fg_rect.x += c->ox;
  fg_rect.y += c->oy;
  fg_rect = rect_intersect(&fg_rect, &r_save);
  canvas_set_clip_rect(c, &fg_rect);
  if (vg != NULL) {
    vgcanvas_clip_rect(vg, fg_rect.x, fg_rect.y, fg_rect.w, fg_rect.h);
  }
  canvas_fill_rounded_rect(c, bg_rect, NULL, color, radius);

  if (vg != NULL) {
    vgcanvas_clip_rect(vg, r_vg_save.x, r_vg_save.y, r_vg_save.w, r_vg_save.h);
    vgcanvas_restore(vg);
  }
  canvas_set_clip_rect(c, &r_save);
  canvas_restore(c);
  return RET_OK;
}

static ret_t setting_slider_fill_rect(widget_t* widget, canvas_t* c, rect_t* r, rect_t* br,
                                      image_draw_type_t draw_type) {
  bitmap_t img;
  style_t* style = widget->astyle;
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  color_t trans = color_init(0, 0, 0, 0);
  uint32_t radius = style_get_int(style, STYLE_ID_ROUND_RADIUS, 0);
  const char* color_key = br ? STYLE_ID_FG_COLOR : STYLE_ID_BG_COLOR;
  const char* image_key = br ? STYLE_ID_FG_IMAGE : STYLE_ID_BG_IMAGE;
  const char* draw_type_key = br ? STYLE_ID_FG_IMAGE_DRAW_TYPE : STYLE_ID_BG_IMAGE_DRAW_TYPE;

  color_t color = style_get_color(style, color_key, trans);
  const char* image_name = style_get_str(style, image_key, NULL);

  if (color.rgba.a && r->w > 0 && r->h > 0) {
    ret_t ret = RET_FAIL;
    canvas_set_fill_color(c, color);
    if (radius > 3) {
      if (tk_str_eq(setting_slider->line_cap, VGCANVAS_LINE_CAP_BUTT) && br) {
        ret = setting_slider_fill_fg_rounded_rect_by_butt(c, *r, br, &color, radius);
      } else {
        ret = canvas_fill_rounded_rect(c, r, br, &color, radius);
      }
      if (ret != RET_OK) {
        canvas_fill_rect(c, r->x, r->y, r->w, r->h);
      }
    } else {
      canvas_fill_rect(c, r->x, r->y, r->w, r->h);
    }
  }

  return RET_OK;
}

static ret_t setting_slider_paint_dragger(widget_t* widget, canvas_t* c) {
  bitmap_t img;
  color_t color;
  uint32_t radius;
  style_t* style = widget->astyle;
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  rect_t* r = &(setting_slider->dragger_rect);
  color_t trans = color_init(0, 0, 0, 0);

  if (setting_slider->no_dragger_icon) {
    return RET_OK;
  }

  color = style_get_color(style, STYLE_ID_BORDER_COLOR, trans);
  radius = style_get_int(style, STYLE_ID_ROUND_RADIUS, 0);

  if (color.rgba.a) {
    canvas_set_fill_color(c, color);
    if (radius > 3) {
      if (canvas_fill_rounded_rect(c, r, NULL, &color, radius) != RET_OK) {
        canvas_fill_rect(c, r->x, r->y, r->w, r->h);
      }
    } else {
      canvas_fill_rect(c, r->x, r->y, r->w, r->h);
    }
  }

  if (setting_slider_load_icon(widget, &img) == RET_OK) {
    canvas_draw_image_ex(c, &img, IMAGE_DRAW_ICON, r);
  }

  return RET_OK;
}



static ret_t setting_slider_paint_serial(widget_t* widget, canvas_t* c) {
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  rect_t *dr = &(setting_slider->dragger_rect);

  int serial_array_len = str_count(&setting_slider->serial, ";");
  if(serial_array_len == 0) {
    return RET_OK;
  }
  setting_slider_update_serial_array(widget);
  str_t str;
  str_init(&str, 10);
  for(int i = 0; i < serial_array_len; i++){
    int line_y = setting_slider->scales[i].line_y;
    rect_t r = rect_init(SCALE_GAP + dr->x + dr->w, line_y - dr->h / 2, dr->w, dr->h);

    // test rect
    // canvas_set_stroke_color(c, color_init(255, 0, 0, 255));
    // canvas_stroke_rect(c, r.x, r.y, r.w, r.h);

    str_from_int(&str, setting_slider->scales[i].value);
    canvas_draw_utf8_in_rect(c, str.str, &r);

  }

  str_reset(&str);
  return RET_OK;
}

static ret_t setting_slider_get_bar_rect(widget_t* widget, rect_t* br, rect_t* fr) {
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  uint32_t bar_size = 0;
  rect_t* dr = NULL;

  return_value_if_fail(widget != NULL && setting_slider != NULL && br != NULL && fr != NULL,
                       RET_BAD_PARAMS);

  bar_size = setting_slider_get_bar_size(widget);
  dr = &(setting_slider->dragger_rect);

  bar_size = tk_min(bar_size, widget->w);
  br->y = SLIDER_GAP;
  br->w = bar_size;
  br->h = widget->h - SLIDER_GAP * 2;
  br->x = (widget->w - br->w) / 2;

  if(!setting_slider->is_reverse){
    fr->x = br->x;
    fr->y = SLIDER_GAP;
    fr->w = br->w;
    fr->h = dr->y + (dr->h >> 1) - SLIDER_GAP;
  }
  else{
    fr->x = br->x;
    fr->y = dr->y + (dr->h >> 1);
    fr->w = br->w;
    fr->h = br->y + br->h - fr->y;
  }
  return RET_OK;
}

static ret_t setting_slider_check_no_dragger_icon(widget_t* widget) {
  bitmap_t img;
  color_t color;
  style_t* style = widget->astyle;
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  color_t trans = color_init(0, 0, 0, 0);

  ret_t ret = setting_slider_load_icon(widget, &img);
  color = style_get_color(style, STYLE_ID_BORDER_COLOR, trans);

  if (color.rgba.a == 0 && ret != RET_OK) {
    setting_slider->no_dragger_icon = TRUE;
  } else {
    setting_slider->no_dragger_icon = FALSE;
  }
  return RET_OK;
}


static ret_t setting_slider_paint_tag(widget_t* widget, canvas_t* c) {
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  rect_t* dr = &(setting_slider->dragger_rect);
  
  bitmap_t img;
  style_t* style = widget->astyle;
  const char* image_name = style_get_str(style, STYLE_ID_BG_IMAGE, NULL);

  if(widget_load_image(widget, image_name, &img) == RET_OK){
    rect_t r = {dr->x + dr->w / 2 - img.w, dr->y + dr->h / 2, img.w, img.h};
    setting_slider->tag_rect = r;
    canvas_draw_image_ex(c, &img, IMAGE_DRAW_DEFAULT, &r);
    // test rect
    // canvas_set_stroke_color(c, color_init(255, 0, 0, 255));
    // canvas_stroke_rect(c, r.x, r.y, r.w, r.h);


    str_t str;
    str_init(&str, 255);
    str_from_int(&str, setting_slider->value);

    style_t *style = widget->astyle;
    int font_size = style_get_int(style, STYLE_ID_HIGHLIGHT_FONT_SIZE, 18);
    color_t font_color = style_get_color(style, STYLE_ID_HIGHLIGHT_TEXT_COLOR, color_init(0, 0, 0, 255));
    const char *font_name = style_get_str(style, STYLE_ID_HIGHLIGHT_FONT_NAME, "default");

    canvas_set_font(c, font_name, font_size);
    canvas_set_text_color(c, font_color);
    canvas_set_text_align(c, ALIGN_H_CENTER, ALIGN_V_MIDDLE);
    rect_t tag_r = {dr->x + dr->w / 2 - img.w, dr->y + dr->h / 2, TAG_CIRCLE_WIDTH, img.h};
    // test rect
    // canvas_set_stroke_color(c, color_init(0, 255, 0, 255));
    // canvas_stroke_rect(c, tag_r.x, tag_r.y, tag_r.w, tag_r.h);
    canvas_draw_utf8_in_rect(c, str.str, &tag_r);
    
    str_reset(&str);
  }


  return RET_OK;
}

static ret_t setting_slider_on_paint_self(widget_t* widget, canvas_t* c) {
  rect_t br, fr;
  image_draw_type_t draw_type;
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);

  style_t *style = widget->astyle;
  int font_size = style_get_int(style, STYLE_ID_FONT_SIZE, 18);
  color_t font_color = style_get_color(style, STYLE_ID_TEXT_COLOR, color_init(0, 0, 0, 255));
  const char *font_name = style_get_str(style, STYLE_ID_FONT_NAME, "default");
  /* canvas以下三行必须要加，否则canvas绘制字体的api会有异常，运行时字体没法马上显示 */
  canvas_set_font(c, font_name, font_size);
  canvas_set_text_color(c, font_color);
  canvas_set_text_align(c, ALIGN_H_LEFT, ALIGN_V_MIDDLE);

  setting_slider_check_no_dragger_icon(widget);
  setting_slider_update_dragger_rect(widget, c);

  draw_type = IMAGE_DRAW_PATCH3_Y;

  return_value_if_fail(RET_OK == setting_slider_get_bar_rect(widget, &br, &fr), RET_FAIL);

  setting_slider_fill_rect(widget, c, &br, NULL, draw_type);
  setting_slider_fill_rect(widget, c, &fr, &br, draw_type);
  setting_slider_paint_dragger(widget, c);

  setting_slider_paint_serial(widget, c);
  setting_slider_paint_tag(widget, c);
  return RET_OK;
}

static ret_t setting_slider_pointer_up_cleanup(widget_t* widget) {
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(widget != NULL && setting_slider != NULL, RET_BAD_PARAMS);
  setting_slider->pressed = FALSE;
  setting_slider->dragging = FALSE;
  widget_ungrab(widget->parent, widget);
  widget_set_state(widget, WIDGET_STATE_NORMAL);
  widget_invalidate(widget, NULL);

  return RET_OK;
}

static ret_t setting_slider_add_value(widget_t* widget, double delta) {
  double new_value = 0;
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(widget != NULL && setting_slider != NULL, RET_BAD_PARAMS);

  new_value = setting_slider->value + delta;

  if (new_value < setting_slider->min) {
    new_value = setting_slider->min;
  }

  if (new_value > setting_slider->max) {
    new_value = setting_slider->max;
  }

  return setting_slider_set_value(widget, new_value);
}

ret_t setting_slider_inc(widget_t* widget) {
  ret_t ret = RET_OK;
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(widget != NULL && setting_slider != NULL, RET_BAD_PARAMS);

  ret = setting_slider_add_value(widget, setting_slider->step ? setting_slider->step : 1);
  return ret;
}

ret_t setting_slider_dec(widget_t* widget) {
  ret_t ret = RET_OK;
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(widget != NULL && setting_slider != NULL, RET_BAD_PARAMS);
  
  ret = setting_slider_add_value(widget, setting_slider->step ? -setting_slider->step : -1);
  return ret;
}

static ret_t setting_slider_change_value_by_pointer_event(widget_t* widget, pointer_event_t* evt) {
  double value = 0;
  point_t p = {evt->x, evt->y};
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  double range = setting_slider->max - setting_slider->min;
  uint32_t dragger_size = setting_slider_get_dragger_size(widget);
  int32_t margin = setting_slider->no_dragger_icon ? 0 : style_get_int(widget->astyle, STYLE_ID_MARGIN, 0);

  widget_to_local(widget, &p);

  value = setting_slider_pixel_to_value(widget, p.y);
  value = tk_clamp(value, setting_slider->min, setting_slider->max);
  return setting_slider_set_value_internal(widget, (double)value, EVT_VALUE_CHANGING, FALSE);
}

static ret_t setting_slider_on_event(widget_t* widget, event_t* e) {
  ret_t ret = RET_OK;
  uint16_t type = e->type;
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(widget != NULL && setting_slider != NULL, RET_BAD_PARAMS);

  ret = setting_slider->dragging ? RET_STOP : RET_OK;
  switch (type) {
    case EVT_POINTER_DOWN: {
      if (!widget_find_animator(widget, WIDGET_PROP_VALUE)) {
        rect_t br, fr;
        pointer_event_t* evt = (pointer_event_t*)e;
        point_t p = {evt->x, evt->y};
        rect_t dr = setting_slider->dragger_rect;
        // Expand the touchable area for better control
        dr.x = setting_slider->tag_rect.x;
        dr.y = setting_slider->dragger_rect.y;
        dr.w = setting_slider->tag_rect.w + setting_slider->dragger_rect.w;
        dr.h = setting_slider->tag_rect.h + setting_slider->dragger_rect.h;
        return_value_if_fail(RET_OK == setting_slider_get_bar_rect(widget, &br, &fr), RET_STOP);

        widget_to_local(widget, &p);
        setting_slider->saved_value = setting_slider->value;
        if (setting_slider->slide_with_bar || rect_contains(&dr, p.x, p.y) || rect_contains(&br, p.x, p.y) ||
            rect_contains(&fr, p.x, p.y)) {
          setting_slider_change_value_by_pointer_event(widget, evt);

          setting_slider->down = p;
          setting_slider->pressed = TRUE;
          widget_set_state(widget, WIDGET_STATE_PRESSED);
          widget_grab(widget->parent, widget);
          widget_invalidate(widget, NULL);
        }
      }
      ret = setting_slider->pressed ? RET_STOP : RET_OK;
      break;
    }
    case EVT_POINTER_DOWN_ABORT: {
      setting_slider_pointer_up_cleanup(widget);
      break;
    }
    case EVT_POINTER_MOVE: {
      pointer_event_t* evt = (pointer_event_t*)e;
      if (setting_slider->dragging) {
        setting_slider_change_value_by_pointer_event(widget, evt);
      } else if(evt->pressed && setting_slider->pressed) {
        int32_t delta;
        point_t p = {evt->x, evt->y};
        widget_to_local(widget, &p);
        delta = evt->y - setting_slider->down.y;

        
        if (tk_abs(delta) > setting_slider->drag_threshold) {
          pointer_event_t abort;
          pointer_event_init(&abort, EVT_POINTER_DOWN_ABORT, widget, evt->x, evt->y);
          widget_dispatch_event_to_target_recursive(widget, (event_t*)(&abort));
          setting_slider->dragging = TRUE;
          setting_slider_change_value_by_pointer_event(widget, evt);
        }
      }

      break;
    }
    case EVT_POINTER_UP: {
      if (setting_slider->dragging || setting_slider->pressed) {
        double value = 0;
        pointer_event_t* evt = (pointer_event_t*)e;

        setting_slider_change_value_by_pointer_event(widget, evt);

        value = setting_slider->value;
        setting_slider->dragging = FALSE;
        setting_slider->value = setting_slider->saved_value;
        setting_slider_set_value(widget, value);
      }
      setting_slider_pointer_up_cleanup(widget);
      break;
    }
    case EVT_POINTER_LEAVE:
      widget_set_state(widget, setting_slider->dragging ? WIDGET_STATE_PRESSED : WIDGET_STATE_NORMAL);
      break;
    case EVT_POINTER_ENTER:
      widget_set_state(widget, setting_slider->dragging ? WIDGET_STATE_PRESSED : WIDGET_STATE_OVER);
      break;
    case EVT_KEY_LONG_PRESS: {
      setting_slider->is_key_long_pressed = TRUE;
      break;
    }
    case EVT_KEY_DOWN: {
      key_event_t* evt = (key_event_t*)e;
      int step = setting_slider->step ? setting_slider->step : 1;
      if(!setting_slider->is_reverse){  
        if (evt->key == TK_KEY_UP) {
          setting_slider_add_value(widget, -step);
          ret = RET_STOP;
        } else if (evt->key == TK_KEY_DOWN) {
          setting_slider_add_value(widget, step);
          ret = RET_STOP;
        }
      }
      else{
        if (evt->key == TK_KEY_UP) {
          setting_slider_add_value(widget, step);
          ret = RET_STOP;
        } else if (evt->key == TK_KEY_DOWN) {
          setting_slider_add_value(widget, -step);
          ret = RET_STOP;
        }       
      }
      break;
    }
    case EVT_KEY_UP: {
      key_event_t* evt = (key_event_t*)e;
      setting_slider->is_key_long_pressed = FALSE;
      if(evt->key == TK_KEY_UP){
        ret = RET_STOP;
      }
      else if(evt->key == TK_KEY_DOWN){
        ret = RET_STOP;
      }
      break;
    }
    case EVT_RESIZE:
    case EVT_MOVE_RESIZE: {
      setting_slider_update_dragger_rect(widget, NULL);
    }
    default: {
      ret = RET_OK;
      break;
    }
  }

  return ret;
}

ret_t setting_slider_set_value_internal(widget_t* widget, double value, event_type_t etype,
                                        bool_t force) {
  double step = 0;
  double offset = 0;
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(setting_slider != NULL, RET_BAD_PARAMS);

  step = setting_slider->step;
  value = tk_clamp(value, setting_slider->min, setting_slider->max);
  
  if (setting_slider->value != value || force) {
    value_change_event_t evt;
    value_change_event_init(&evt, etype, widget);
    value_set_double(&(evt.old_value), setting_slider->value);
    value_set_double(&(evt.new_value), value);
    setting_slider->value = value;
    widget_dispatch(widget, (event_t*)&evt);
    widget_invalidate(widget, NULL);
  }

  return RET_OK;
}

ret_t setting_slider_set_value(widget_t* widget, double value) {
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(setting_slider != NULL, RET_BAD_PARAMS);

  if (setting_slider->dragging) {
    return RET_BUSY;
  }
  
  if (setting_slider->value != value) {
    value_change_event_t evt;
    value_change_event_init(&evt, EVT_VALUE_WILL_CHANGE, widget);
    value_set_uint32(&(evt.old_value), setting_slider->value);
    value_set_uint32(&(evt.new_value), value);
    if (widget_dispatch(widget, (event_t*)&evt) == RET_STOP) {
      return RET_OK;
    }

    return setting_slider_set_value_internal(widget, value, EVT_VALUE_CHANGED, FALSE);
  }

  return RET_OK;
}

ret_t setting_slider_set_min(widget_t* widget, double min) {
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(setting_slider != NULL, RET_BAD_PARAMS);

  setting_slider->min = min;

  return widget_invalidate(widget, NULL);
}

ret_t setting_slider_set_max(widget_t* widget, double max) {
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(setting_slider != NULL, RET_BAD_PARAMS);

  setting_slider->max = max;

  return widget_invalidate(widget, NULL);
}

ret_t setting_slider_set_step(widget_t* widget, double step) {
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(setting_slider != NULL && step >= 0, RET_BAD_PARAMS);

  setting_slider->step = step;

  return widget_invalidate(widget, NULL);
}

ret_t setting_slider_set_bar_size(widget_t* widget, uint32_t bar_size) {
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(setting_slider != NULL, RET_BAD_PARAMS);

  setting_slider->bar_size = bar_size;

  return widget_invalidate(widget, NULL);
}

ret_t setting_slider_set_line_cap(widget_t* widget, const char* line_cap) {
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(setting_slider != NULL, RET_BAD_PARAMS);

  setting_slider->line_cap = tk_str_copy(setting_slider->line_cap, line_cap);
  return widget_invalidate(widget, NULL);
}

ret_t setting_slider_set_drag_threshold(widget_t* widget, uint32_t drag_threshold) {
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(setting_slider != NULL, RET_BAD_PARAMS);

  setting_slider->drag_threshold = drag_threshold;
  return RET_OK;
}

static ret_t setting_slider_get_prop(widget_t* widget, const char* name, value_t* v) {
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(setting_slider != NULL && name != NULL && v != NULL, RET_BAD_PARAMS);

  if (tk_str_eq(name, WIDGET_PROP_VALUE)) {
    value_set_double(v, setting_slider->value);
    return RET_OK;
  } else if (tk_str_eq(name, WIDGET_PROP_STEP)) {
    value_set_double(v, setting_slider->step);
    return RET_OK;
  } else if (tk_str_eq(name, WIDGET_PROP_MIN)) {
    value_set_double(v, setting_slider->min);
    return RET_OK;
  } else if (tk_str_eq(name, WIDGET_PROP_MAX)) {
    value_set_double(v, setting_slider->max);
    return RET_OK;
  } else if (tk_str_eq(name, WIDGET_PROP_BAR_SIZE)) {
    value_set_uint32(v, setting_slider->bar_size);
    return RET_OK;
  } else if (tk_str_eq(name, SETTING_SLIDER_PROP_DRAGGER_SIZE)) {
    value_set_uint32(v, setting_slider->dragger_size);
    return RET_OK;
  } else if (tk_str_eq(name, SETTING_SLIDER_PROP_DRAGGER_ADAPT_TO_ICON)) {
    value_set_bool(v, setting_slider->dragger_adapt_to_icon);
    return RET_OK;
  } else if (tk_str_eq(name, SETTING_SLIDER_PROP_SLIDE_WITH_BAR)) {
    value_set_bool(v, setting_slider->slide_with_bar);
    return RET_OK;
  } else if (tk_str_eq(name, WIDGET_PROP_INPUTING)) {
    value_set_bool(v, setting_slider->dragging);
    return RET_OK;
  } else if (tk_str_eq(name, SETTING_SLIDER_PROP_SLIDE_LINE_CAP)) {
    value_set_str(v, setting_slider->line_cap);
    return RET_OK;
  } else if (tk_str_eq(name, SETTING_SLIDER_PROP_SERIAL)) {
    value_set_str(v, setting_slider->serial.str);
    return RET_OK;
  } else if (tk_str_eq(name, SETTING_SLIDER_PROP_IS_REVERSE)) {
    value_set_bool(v, setting_slider->is_reverse);
    return RET_OK;
  } else if (tk_str_eq(name, WIDGET_PROP_DRAG_THRESHOLD)){
    value_set_uint32(v, setting_slider->drag_threshold);
    return RET_OK;
  }

  return RET_NOT_FOUND;
}

static ret_t setting_slider_set_prop(widget_t* widget, const char* name, const value_t* v) {
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(setting_slider != NULL && name != NULL && v != NULL, RET_BAD_PARAMS);

  if (tk_str_eq(name, WIDGET_PROP_VALUE)) {
    return setting_slider_set_value(widget, value_double(v));
  } else if (tk_str_eq(name, WIDGET_PROP_STEP)) {
    return setting_slider_set_step(widget, value_double(v));
  } else if (tk_str_eq(name, WIDGET_PROP_MIN)) {
    return setting_slider_set_min(widget, value_double(v));
  } else if (tk_str_eq(name, WIDGET_PROP_MAX)) {
    return setting_slider_set_max(widget, value_double(v));
  } else if (tk_str_eq(name, WIDGET_PROP_BAR_SIZE)) {
    return setting_slider_set_bar_size(widget, value_uint32(v));
  } else if (tk_str_eq(name, SETTING_SLIDER_PROP_DRAGGER_SIZE)) {
    setting_slider->dragger_size = value_uint32(v);
    setting_slider->auto_get_dragger_size = setting_slider->dragger_size == 0;
    return RET_OK;
  } else if (tk_str_eq(name, SETTING_SLIDER_PROP_DRAGGER_ADAPT_TO_ICON)) {
    setting_slider->dragger_adapt_to_icon = value_bool(v);
    return RET_OK;
  } else if (tk_str_eq(name, SETTING_SLIDER_PROP_SLIDE_WITH_BAR)) {
    setting_slider->slide_with_bar = value_bool(v);
    return RET_OK;
  } else if (tk_str_eq(name, SETTING_SLIDER_PROP_SLIDE_LINE_CAP)) {
    return setting_slider_set_line_cap(widget, value_str(v));
  } else if (tk_str_eq(name, SETTING_SLIDER_PROP_SERIAL)) {
    str_from_value(&setting_slider->serial, v);
    setting_slider_update_serial_array(widget);
    return RET_OK;
  } else if (tk_str_eq(name, SETTING_SLIDER_PROP_IS_REVERSE)) {
    setting_slider->is_reverse = value_bool(v);
    setting_slider_update_serial_array(widget);
    return RET_OK;
  } else if (tk_str_eq(name, WIDGET_PROP_DRAG_THRESHOLD)) {
    return setting_slider_set_drag_threshold(setting_slider, value_uint32(v));
  }

  return RET_NOT_FOUND;
}




static ret_t setting_slider_update_serial_array(widget_t* widget) {
    setting_slider_t* setting_slider = SETTING_SLIDER(widget);

    tokenizer_t tokenizer;
    tokenizer_t* t = tokenizer_init(&tokenizer, setting_slider->serial.str, 0xffffff, ";");
    int serial_array_len = setting_slider_get_serial_count(widget);
    uint32_t dragger_size = setting_slider_get_dragger_size(widget);
    double start_line = setting_slider_get_top_y(widget), end_line = setting_slider_get_bottom_y(widget);
    double line_width = setting_slider_get_serial_line_width(widget);
    
    setting_slider->min = INT_MAX;
    setting_slider->max = INT_MIN;

    /* Collect all values first to determine min/max */
    int values[SETTING_SLIDER_MAX_SCALE_NUM];
    int idx = 0;
    while (tokenizer_has_more(t) && idx < serial_array_len) {
      values[idx] = tokenizer_next_int(t, 0);
      setting_slider->min = tk_min(setting_slider->min, values[idx]);
      setting_slider->max = tk_max(setting_slider->max, values[idx]);
      idx++;
    }
    tokenizer_deinit(t);
    
  /* Assign positions based on is_reverse setting */
  /**
   * natural: 
   * upper_value lower_value upper_pos low_pos
   *    60            40        184      156
   *    80            60        212      184
   * 
   * 
   * reverse: 
   * upper_value lower_value upper_pos low_pos
   *    60            80        160      131
   *    40            60        189      160
   * 
   */
    double p_line = start_line;
    if(!setting_slider->is_reverse){
      for (int i = 0; i < idx; i++) {
        setting_slider->scales[i].value = values[i];
        setting_slider->scales[i].line_y = p_line;
        p_line += line_width;
      }
    }
    else{
      for (int i = 0; i < idx; i++) {
        setting_slider->scales[i].value = values[idx - 1 - i];
        setting_slider->scales[i].line_y = p_line;
        p_line += line_width;
      }
    }

    widget_invalidate_force(widget, FALSE);
    return RET_OK;
}

static ret_t setting_slider_on_destroy(widget_t* widget) {
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(setting_slider != NULL, RET_BAD_PARAMS);

  if (setting_slider->line_cap != NULL) {
    TKMEM_FREE(setting_slider->line_cap);
  }

  str_reset(&(setting_slider->serial));
  return RET_OK;
}

static const char* s_setting_slider_properties[] = {
                                                    WIDGET_PROP_VERTICAL,
                                                    WIDGET_PROP_MIN,
                                                    WIDGET_PROP_MAX,
                                                    WIDGET_PROP_VALUE,
                                                    WIDGET_PROP_STEP,
                                                    WIDGET_PROP_BAR_SIZE,
                                                    SETTING_SLIDER_PROP_DRAGGER_SIZE,
                                                    SETTING_SLIDER_PROP_DRAGGER_ADAPT_TO_ICON,
                                                    SETTING_SLIDER_PROP_SLIDE_WITH_BAR,
                                                    SETTING_SLIDER_PROP_IS_REVERSE,
                                                    WIDGET_PROP_DRAG_THRESHOLD,
                                                    NULL};

TK_DECL_VTABLE(setting_slider) = {.size = sizeof(setting_slider_t),
                                  .type = WIDGET_TYPE_SETTING_SLIDER,
                                  .inputable = TRUE,
                                  .clone_properties = s_setting_slider_properties,
                                  .persistent_properties = s_setting_slider_properties,
                                  .get_parent_vt = TK_GET_PARENT_VTABLE(widget),
                                  .create = setting_slider_create,
                                  .on_event = setting_slider_on_event,
                                  .on_paint_self = setting_slider_on_paint_self,
                                  .on_paint_border = widget_on_paint_null,
                                  .on_paint_background = widget_on_paint_null,
                                  .on_destroy = setting_slider_on_destroy,
                                  .get_prop = setting_slider_get_prop,
                                  .set_prop = setting_slider_set_prop};

widget_t* setting_slider_create(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h) {
  widget_t* widget = widget_create(parent, TK_REF_VTABLE(setting_slider), x, y, w, h);
  setting_slider_t* setting_slider = SETTING_SLIDER(widget);
  return_value_if_fail(setting_slider != NULL, NULL);
  
  setting_slider->min = 0;
  setting_slider->max = 10000;
  setting_slider->step = 1;
  setting_slider->value = 0;
  setting_slider->auto_get_dragger_size = TRUE;
  setting_slider->dragger_adapt_to_icon = TRUE;
  setting_slider->slide_with_bar = FALSE;
  setting_slider->is_reverse = FALSE;
  str_init(&setting_slider->serial, 255);
  return widget;
}

widget_t* setting_slider_cast(widget_t* widget) {
  return_value_if_fail(WIDGET_IS_INSTANCE_OF(widget, setting_slider), NULL);

  return widget;
}
